﻿using System.Collections.Generic;
using System.IO;

using Vestras.StarCraft2.Luna.Parser;
using Vestras.StarCraft2.Luna.Parser.Ast;
using Vestras.StarCraft2.Luna.Validation;

namespace Vestras.StarCraft2.Luna.Compiler {
    class Program {
        class Argument {
            public string Id { get; private set; }
            public string Value { get; private set; }

            public Argument(string id, string value) {
                if (string.IsNullOrEmpty(id)) {
                    throw new System.ArgumentNullException("id");
                }

                if (string.IsNullOrEmpty(value)) {
                    throw new System.ArgumentNullException("value");
                }

                Id = id;
                Value = value;
            }

            public override string ToString() {
                return Id + ":" + "\"" + Value + "\"";
            }
        }

        static IEnumerable<Argument> ProcessArgs(string[] args, out bool error) {
            error = false;
            List<Argument> processedArgs = new List<Argument>();
            foreach (string arg in args) {
                int firstColon = arg.IndexOf(':');
                List<string> argList = new List<string>();
                argList.Add(arg.Substring(0, firstColon));
                argList.Add(arg.Substring(firstColon + 1, arg.Length - (firstColon + 1)));
                string[] splitArg = argList.ToArray();
                if (splitArg.Length != 2) {
                    error = true;
                    break;
                }
                processedArgs.Add(new Argument(splitArg[0], splitArg[1]));
            }

            return processedArgs;
        }

        static void AddFilesToInputFiles(DirectoryInfo dirInfo, List<string> inputFiles) {
            foreach (FileInfo fileInfo in dirInfo.GetFiles("*.galaxy")) {
                inputFiles.Add(fileInfo.FullName);
            }

            foreach (DirectoryInfo childDirInfo in dirInfo.GetDirectories()) {
                AddFilesToInputFiles(childDirInfo, inputFiles);
            }
        }

        static void Main(string[] args) {
            const string BaseErrorMessage = "An error occurred reading command line arguments. Aborting...";
            bool hasError;
            IEnumerable<Argument> processedArgs = ProcessArgs(args, out hasError);
            if (hasError) {
                System.Console.WriteLine(BaseErrorMessage);
                return;
            }

            List<string> inputFiles = new List<string>();
            string outputFile = "";
            bool generateCode = true;
            foreach (Argument argument in processedArgs) {
                if (argument.Id == "i" || argument.Id == "input") {
                    if (!File.Exists(argument.Value)) {
                        System.Console.WriteLine(BaseErrorMessage + " (Unable to find specified file: " + argument.Value + ")");
                        return;
                    }

                    inputFiles.Add(argument.Value);
                } else if (argument.Id == "d" || argument.Id == "dir") {
                    if (!Directory.Exists(argument.Value)) {
                        System.Console.WriteLine(BaseErrorMessage + " (Unable to find specified directory: " + argument.Value + ")");
                        return;
                    }

                    foreach (FileInfo info in new DirectoryInfo(argument.Value).GetFiles("*.galaxy")) {
                        inputFiles.Add(info.FullName);
                    }
                } else if (argument.Id == "o" || argument.Id == "output") {
                    outputFile = argument.Value;
                } else if (argument.Id == "gc" || argument.Id == "generate") {
                    bool result;
                    if (!bool.TryParse(argument.Value, out result)) {
                        System.Console.WriteLine(BaseErrorMessage);
                        return;
                    }

                    generateCode = result;
                }
            }

            DirectoryInfo libDirectory = new DirectoryInfo("lib");
            if (libDirectory.Exists) {
                AddFilesToInputFiles(libDirectory, inputFiles);
            }

            if (inputFiles.Count == 0) {
                System.Console.WriteLine("No input files specified.");
                return;
            }

            System.DateTime startCompilationTime = System.DateTime.Now;
            FileNetwork network = new FileNetwork(inputFiles);
            bool dummyValid;
            IEnumerable<Entity> entities = FileNetworkParser.Instance.ParseNetwork(network, true, out dummyValid);
            ValidationEntryPoint entryPoint = new ValidationEntryPoint(entities);
            bool valid = entryPoint.StartValidation();
            System.Console.WriteLine(string.Format("Luna Galaxy compiler version {0} - lc.exe{1}Kopime (K) Theodor \"Vestras\" Storm Kristensen. All lefts reserved.{1}{1}Compilation took {2} milliseconds.", typeof(Program).Assembly.GetName().Version.ToString(), System.Environment.NewLine, (System.DateTime.Now - startCompilationTime).Milliseconds));
            if (valid) {
                System.Console.WriteLine("The compilation was successful." + System.Environment.NewLine);
            } else {
                System.Console.WriteLine("The compilation was unsuccessful." + System.Environment.NewLine + ErrorSink.Errors.Count + " errors were found." + System.Environment.NewLine);
            }

            foreach (ErrorSink.Error error in ErrorSink.Errors) {
                System.Console.WriteLine(error.ToString());
                System.Diagnostics.Debug.Write(error.ToString() + System.Environment.NewLine);
            }
        }
    }
}
